"""
热量消耗记录 Service 层
负责简单业务校验和逻辑封装，依赖 DAO 层交互数据库
返回格式：(bool, f"提示信息")，True 表示操作成功，False 表示失败
"""
from datetime import date, datetime
from typing import Optional

from app.Dao.HealthDao.KcalRecordDao import KcalRecordDAO
from app.Models.HealthModels.KcalRecordModel import KcalRecord


class KcalRecordServices:
    @staticmethod
    def add_record(
            user_id: int,
            kcal_date: date,
            food_intake_kcal: float = 0.0,
            sport_consume_kcal: float = 0.0,
            act_kcal_gap: float = 0.0,
            ideal_sub_weight: float = 0.0
    ) -> tuple[bool, str]:
        """
        新增热量记录
        :param user_id: 用户ID（必传）
        :param kcal_date: 记录日期（必传）
        :param food_intake_kcal: 食物摄入热量（默认0.0）
        :param sport_consume_kcal: 运动消耗热量（默认0.0）
        :param act_kcal_gap: 实际热量缺口（默认0.0）
        :param ideal_sub_weight: 理论可减体重（默认0.0）
        :return: (是否成功, 提示信息)
        """
        # 简单参数校验
        if not user_id or not isinstance(user_id, int) or user_id <= 0:
            return False, f"用户ID无效（当前值：{user_id}），需传入正整数"
        if not kcal_date or not isinstance(kcal_date, date):
            return False, f"记录日期无效（当前值：{kcal_date}），需传入date类型"
        # 校验日期合理性（不允许未来日期，可根据业务调整）
        if kcal_date > date.today():
            return False, f"记录日期不能晚于当前日期（当前日期：{date.today().strftime('%Y-%m-%d')}）"

        # 校验用户该日期是否已存在记录（避免重复）
        exist_record = KcalRecordDAO.get_by_user_date(user_id, kcal_date)
        if exist_record:
            return False, f"用户{user_id}在{kcal_date.strftime('%Y-%m-%d')}已存在热量记录，不可重复添加"

        # 构建记录实例
        record = KcalRecord(
            user_id=user_id,
            kcal_date=kcal_date,
            food_intake_kcal=food_intake_kcal,
            sport_consume_kcal=sport_consume_kcal,
            act_kcal_gap=act_kcal_gap,
            ideal_sub_weight=ideal_sub_weight
        )

        # 调用DAO层新增
        success = KcalRecordDAO.add(record)
        if success:
            return True, f"用户{user_id}在{kcal_date.strftime('%Y-%m-%d')}的热量记录新增成功"
        else:
            return False, f"用户{user_id}在{kcal_date.strftime('%Y-%m-%d')}的热量记录新增失败"

    @staticmethod
    def delete_record(record_id: int) -> tuple[bool, str]:
        """
        根据记录ID删除热量记录
        :param record_id: 记录主键ID
        :return: (是否成功, 提示信息)
        """
        # 参数校验
        if not record_id or not isinstance(record_id, int) or record_id <= 0:
            return False, f"记录ID无效（当前值：{record_id}），需传入正整数"

        # 调用DAO层删除
        success = KcalRecordDAO.delete(record_id)
        if success:
            return True, f"记录ID{record_id}的热量记录删除成功"
        else:
            return False, f"记录ID{record_id}的热量记录删除失败"

    @staticmethod
    def update_record(
            record_id: int,
            user_id: Optional[int] = None,
            kcal_date: Optional[date] = None,
            food_intake_kcal: Optional[float] = None,
            sport_consume_kcal: Optional[float] = None,
            act_kcal_gap: Optional[float] = None,
            ideal_sub_weight: Optional[float] = None
    ) -> tuple[bool, str]:
        """
        更新热量记录（支持部分字段更新）
        :param record_id: 记录ID（必传）
        :param user_id: 用户ID（可选，不允许修改时可删除该参数）
        :param kcal_date: 记录日期（可选）
        :param food_intake_kcal: 食物摄入热量（可选）
        :param sport_consume_kcal: 运动消耗热量（可选）
        :param act_kcal_gap: 实际热量缺口（可选）
        :param ideal_sub_weight: 理论可减体重（可选）
        :return: (是否成功, 提示信息)
        """
        # 参数校验
        if not record_id or not isinstance(record_id, int) or record_id <= 0:
            return False, f"记录ID无效（当前值：{record_id}），需传入正整数"

        # 查询记录是否存在
        exist_record = KcalRecordDAO.get_by_id(record_id)
        if not exist_record:
            return False, f"记录ID{record_id}的热量记录不存在，无法更新"

        # 日期合理性校验（若修改日期）
        if kcal_date:
            if not isinstance(kcal_date, date):
                return False, f"记录日期无效（当前值：{kcal_date}），需传入date类型"
            if kcal_date > date.today():
                return False, f"记录日期不能晚于当前日期（当前日期：{date.today().strftime('%Y-%m-%d')}）"
            # 校验新日期是否与其他记录冲突
            target_user_id = user_id or exist_record.user_id
            conflict_record = KcalRecordDAO.get_by_user_date(target_user_id, kcal_date)
            if conflict_record and conflict_record.id != record_id:
                return False, f"用户{target_user_id}在{kcal_date.strftime('%Y-%m-%d')}已存在热量记录，不可重复"

        # 更新字段（仅更新非None的参数）
        if user_id is not None and isinstance(user_id, int) and user_id > 0:
            exist_record.user_id = user_id
        if kcal_date:
            exist_record.kcal_date = kcal_date
        if food_intake_kcal is not None:
            exist_record.food_intake_kcal = food_intake_kcal
        if sport_consume_kcal is not None:
            exist_record.sport_consume_kcal = sport_consume_kcal
        if act_kcal_gap is not None:
            exist_record.act_kcal_gap = act_kcal_gap
        if ideal_sub_weight is not None:
            exist_record.ideal_sub_weight = ideal_sub_weight

        # 调用DAO层更新
        success = KcalRecordDAO.update(exist_record)
        if success:
            return True, f"记录ID{record_id}的热量记录更新成功"
        else:
            return False, f"记录ID{record_id}的热量记录更新失败"

    @staticmethod
    def get_record_by_id(record_id: int) -> tuple[Optional[KcalRecord], str]:
        """
        根据ID查询单条记录
        :param record_id: 记录ID
        :return: (记录实例/None, 提示信息)
        """
        if not record_id or not isinstance(record_id, int) or record_id <= 0:
            return None, f"记录ID无效（当前值：{record_id}），需传入正整数"

        record = KcalRecordDAO.get_by_id(record_id)
        if record:
            return record, f"记录ID{record_id}查询成功"
        else:
            return None, f"记录ID{record_id}不存在或查询失败"

    @staticmethod
    def get_record_by_user_date(user_id: int, kcal_date: date) -> tuple[Optional[KcalRecord], str]:
        """
        根据用户ID和日期查询记录
        :param user_id: 用户ID
        :param kcal_date: 记录日期
        :return: (记录实例/None, 提示信息)
        """
        if not user_id or not isinstance(user_id, int) or user_id <= 0:
            return None, f"用户ID无效（当前值：{user_id}），需传入正整数"
        if not kcal_date or not isinstance(kcal_date, date):
            return None, f"记录日期无效（当前值：{kcal_date}），需传入date类型"

        record = KcalRecordDAO.get_by_user_date(user_id, kcal_date)
        if record:
            return record, f"用户{user_id}在{kcal_date.strftime('%Y-%m-%d')}的记录查询成功"
        else:
            return None, f"用户{user_id}在{kcal_date.strftime('%Y-%m-%d')}无相关记录或查询失败"

    @staticmethod
    def get_records_by_user(user_id: int, page: int = 1, per_page: int = 30) -> tuple[list[KcalRecord], str]:
        """
        分页查询用户的所有记录（按日期倒序）
        :param user_id: 用户ID
        :param page: 页码（默认1）
        :param per_page: 每页条数（默认30）
        :return: (记录列表, 提示信息)
        """
        if not user_id or not isinstance(user_id, int) or user_id <= 0:
            return [], f"用户ID无效（当前值：{user_id}），需传入正整数"
        if not isinstance(page, int) or page < 1:
            return [], f"页码无效（当前值：{page}），需传入≥1的整数"
        if not isinstance(per_page, int) or per_page < 1 or per_page > 100:
            return [], f"每页条数无效（当前值：{per_page}），需传入1-100的整数"

        records = KcalRecordDAO.get_by_user(user_id, page, per_page)
        if records:
            return records, f"用户{user_id}第{page}页记录查询成功，共{len(records)}条"
        else:
            return [], f"用户{user_id}第{page}页无记录或查询失败"

    @staticmethod
    def get_records_by_date_range(user_id: int, start_date: date, end_date: date) -> tuple[list[KcalRecord], str]:
        """
        根据日期范围查询用户记录（按日期正序）
        :param user_id: 用户ID
        :param start_date: 开始日期
        :param end_date: 结束日期
        :return: (记录列表, 提示信息)
        """
        # 参数校验
        if not user_id or not isinstance(user_id, int) or user_id <= 0:
            return [], f"用户ID无效（当前值：{user_id}），需传入正整数"
        if not start_date or not isinstance(start_date, date):
            return [], f"开始日期无效（当前值：{start_date}），需传入date类型"
        if not end_date or not isinstance(end_date, date):
            return [], f"结束日期无效（当前值：{end_date}），需传入date类型"
        if start_date > end_date:
            return [], f"开始日期（{start_date.strftime('%Y-%m-%d')}）不能晚于结束日期（{end_date.strftime('%Y-%m-%d')}）"
        if end_date > date.today():
            return [], f"结束日期不能晚于当前日期（当前日期：{date.today().strftime('%Y-%m-%d')}）"

        records = KcalRecordDAO.get_by_date_range(user_id, start_date, end_date)
        if records:
            return records, f"用户{user_id}在{start_date.strftime('%Y-%m-%d')}至{end_date.strftime('%Y-%m-%d')}期间共查询到{len(records)}条记录"
        else:
            return [], f"用户{user_id}在{start_date.strftime('%Y-%m-%d')}至{end_date.strftime('%Y-%m-%d')}期间无记录或查询失败"

    @staticmethod
    def batch_add_records(records: list[dict]) -> tuple[bool, str]:
        """
        批量新增热量记录
        :param records: 记录字典列表，每个字典需包含user_id、kcal_date等必填字段
        :return: (是否成功, 提示信息)
        """
        if not records or not isinstance(records, list):
            return False, f"批量新增失败：传入的记录列表为空或格式无效"

        # 批量参数校验
        valid_records = []
        error_msg = []
        today = date.today()

        for idx, record_dict in enumerate(records, 1):
            # 基础字段校验
            user_id = record_dict.get("user_id")
            kcal_date = record_dict.get("kcal_date")
            if not user_id or not isinstance(user_id, int) or user_id <= 0:
                error_msg.append(f"第{idx}条记录：用户ID无效（值：{user_id}）")
                continue
            if not kcal_date or not isinstance(kcal_date, date):
                error_msg.append(f"第{idx}条记录：日期无效（值：{kcal_date}）")
                continue
            if kcal_date > today:
                error_msg.append(f"第{idx}条记录：日期晚于当前日期（值：{kcal_date.strftime('%Y-%m-%d')}）")
                continue

            # 重复记录校验
            if KcalRecordDAO.get_by_user_date(user_id, kcal_date):
                error_msg.append(f"第{idx}条记录：用户{user_id}在{kcal_date.strftime('%Y-%m-%d')}已存在记录")
                continue

            # 构建实例
            valid_record = KcalRecord(
                user_id=user_id,
                kcal_date=kcal_date,
                food_intake_kcal=record_dict.get("food_intake_kcal", 0.0),
                sport_consume_kcal=record_dict.get("sport_consume_kcal", 0.0),
                act_kcal_gap=record_dict.get("act_kcal_gap", 0.0),
                ideal_sub_weight=record_dict.get("ideal_sub_weight", 0.0)
            )
            valid_records.append(valid_record)

        # 存在校验失败的记录
        if error_msg:
            return False, f"批量新增失败：{'; '.join(error_msg)}"

        # 调用DAO批量新增
        success = KcalRecordDAO.batch_add(valid_records)
        if success:
            return True, f"批量新增成功：共新增{len(valid_records)}条热量记录"
        else:
            return False, f"批量新增失败：数据库操作异常"

    @staticmethod
    def batch_delete_records(record_ids: list[int]) -> tuple[bool, str]:
        """
        批量删除热量记录
        :param record_ids: 记录ID列表
        :return: (是否成功, 提示信息)
        """
        if not record_ids or not isinstance(record_ids, list):
            return False, f"批量删除失败：传入的ID列表为空或格式无效"

        # 校验ID格式
        invalid_ids = [str(id) for id in record_ids if not (isinstance(id, int) and id > 0)]
        if invalid_ids:
            return False, f"批量删除失败：无效ID列表（{', '.join(invalid_ids)}），需传入正整数"

        # 调用DAO批量删除
        success = KcalRecordDAO.batch_delete(record_ids)
        if success:
            return True, f"批量删除成功：共删除{len(record_ids)}条热量记录（ID：{', '.join(map(str, record_ids))}）"
        else:
            return False, f"批量删除失败：数据库操作异常"
